/*
 * @Author: wuqingchun
 * @Date: 2023-08-20 07:18:39
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-01-10 20:01:33
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/select.h>

#include "ftpc.h"
#include "ftpc_log.h"
#include "ftpc_tcp.h"
#include "ftpc_stdin.h"
#include "ftpc_util.h"
#include "ftpc_ctl.h"
#include "ftpc_dtp.h"
#include "ftpc_pi.h"
#include "ftpc_event.h"


static void __select_HandleEvent(fd_set* pstReadFds, fd_set* pstWriteFds, fd_set* pstExceptFds)
{
    int iErr = 0;
    unsigned int uiCount, uiIndex;
    FTPC_EVENT_S* pstEvent;
    FTPC_EVENT_DATA_S* pstItem;
    

    pstEvent = &g_stFTPCClient.stEvent;
    uiCount = pstEvent->uiCount;
    for (uiIndex = 0; uiIndex < FTPC_EVENT_FD_MAX && uiCount > 0; uiIndex++)
    {
        pstItem = pstEvent->astData + uiIndex;

        if (pstItem->iFd == -1)
        {
            continue;
        }

        uiCount--;

        if (FD_ISSET(pstItem->iFd, pstExceptFds))
        {
            FTPC_LOG_ERROR("Happened exception, fd=%d", pstItem->iFd);
        }

        if (FD_ISSET(pstItem->iFd, pstReadFds))
        {
            FTPC_LOG_DEBUG("Ready for read, fd=%d", pstItem->iFd);

            if (pstItem->iFd == STDIN_FILENO)
            {
                // 标准输入
                iErr = FTPC_stdin_Read();
            }
            else if (pstItem->iFd == g_stFTPCClient.iCtlSock)
            {
                // 控制连接
                iErr = FTPC_ctl_RecvResponse();
            }
            else
            {
                // 数据连接
                iErr = FTPC_dtp_Input(pstItem->iFd);
            }
        }

        if (FD_ISSET(pstItem->iFd, pstWriteFds))
        {
            FTPC_LOG_DEBUG("Ready for write, fd=%d", pstItem->iFd);
            FTPC_dtp_Output(pstItem->iFd);
        }
    }
}

static int __select_InitFDSet(fd_set* pstReadFds, fd_set* pstWriteFds, fd_set* pstExceptFds)
{
    int iMaxFd = -1;
    int i;
    unsigned int uiCount;
    FTPC_EVENT_S* pstEvent;
    FTPC_EVENT_DATA_S* pstItem;
    

    FD_ZERO(pstReadFds);
    FD_ZERO(pstWriteFds);
    FD_ZERO(pstExceptFds);

    pstEvent = &g_stFTPCClient.stEvent;
    uiCount = pstEvent->uiCount;

    FTPC_LOG_DEBUG("fd count: %u", uiCount);

    for (i = 0; i < FTPC_EVENT_FD_MAX && uiCount > 0; i++)
    {
        pstItem = pstEvent->astData + i;
        if (pstItem->iFd == -1)
        {
            continue;
        }

        uiCount--;

        FTPC_LOG_DEBUG("  fd: %d, events: 0x%x", pstItem->iFd, pstItem->uiEvents);

        if (BIT_TEST(pstItem->uiEvents, FTPC_EVENT_IN))
        {
            FD_SET(pstItem->iFd, pstReadFds);
        }

        if (BIT_TEST(pstItem->uiEvents, FTPC_EVENT_OUT))
        {
            FD_SET(pstItem->iFd, pstWriteFds);
        }

        FD_SET(pstItem->iFd, pstExceptFds);

        if (iMaxFd < pstItem->iFd)
        {
            iMaxFd = pstItem->iFd;
        }
    }

    return iMaxFd;
}

void FTPC_select_Schedule(void)
{  
    fd_set stReadFds;
    fd_set stWriteFds;
    fd_set stExceptFds;
    int iReadyNum;
    int iMaxFd;

    while (1)
    {
        iMaxFd = __select_InitFDSet(&stReadFds, &stWriteFds, &stExceptFds);
        if (iMaxFd == -1)
        {
            // 没有可以关注的描述符
            if (FTPC_stdin_WaitInput())
            {
                continue;
            }

            FD_SET(STDIN_FILENO, &stReadFds);
            iMaxFd = STDIN_FILENO;
        }

        // 1. ndfs 等待的文件描述符的最大值+1，例如：应用进程想要去等待文件描述符3,5,8的事件，则 nfds=max(3,5,8)+1;
        // 2. select 函数调用前后会修改 readfds、writefds 和 exceptfds 这三个集合中的内容（如果有的话），
        // 所以如果您想下次调用 select 复用这个变量，记得在下次调用前再次调用 select 前先使用 FD_ZERO 将集合清零，
        // 然后调用 FD_SET 将需要检测事件的 fd 再次添加进去。
        iReadyNum = select(iMaxFd + 1, &stReadFds, &stWriteFds, &stExceptFds, NULL);
        if (iReadyNum == -1)
        {
            FTPC_LOG_ERROR("select error, %s", strerror(errno));
            break;
        }
        
        if (iReadyNum > 0)
        {
            __select_HandleEvent(&stReadFds, &stWriteFds, &stExceptFds);
        }        
    }
}